﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Markup;

namespace AppDesigner.XamlDom
{
    #region XamlPropertyInfo

    /// <summary>
    /// represent property in xaml
    /// this can be a normal .net property or an attach property
    /// </summary>
    public abstract class XamlPropertyInfo
    {
        public abstract object GetValue(object instance);
        public abstract void SetValue(object instance, object value);
        public abstract void ResetValue(object instance);
        public abstract TypeConverter TypeConverter { get; }
        public abstract Type TargetType { get; }
        public abstract Type ReturnType { get; }
        public abstract string Name { get; }
        public abstract string FullQualifiedName { get; }
        public abstract bool IsAttached { get; }
        public abstract bool IsCollection { get; }
        public virtual bool IsEvent { get { return false; } }
        public virtual bool IsAdvanced { get { return false; } }
        public virtual DependencyProperty DependencyProperty { get { return null; } }
        public abstract string Category { get; }
    }

    #endregion

    #region XamlNormalPropertyInfo

    public class XamlNormalPropertyInfo : XamlPropertyInfo
    {
        #region Fields

        private PropertyDescriptor _propertyDescriptor;
        private DependencyProperty _dependencyProperty;

        #endregion

        #region Constructor

        public XamlNormalPropertyInfo(PropertyDescriptor propertyDescriptor)
        {
            this._propertyDescriptor = propertyDescriptor;
            var dpd = DependencyPropertyDescriptor.FromProperty(propertyDescriptor);
            if (dpd != null)
            {
                _dependencyProperty = dpd.DependencyProperty;
            }
        }

        #endregion

        #region Implements

        public override object GetValue(object instance)
        {
            return _propertyDescriptor.GetValue(instance);
        }

        public override void SetValue(object instance, object value)
        {
            _propertyDescriptor.SetValue(instance, value);
        }

        public override void ResetValue(object instance)
        {
            _propertyDescriptor.ResetValue(instance);
        }

        public override TypeConverter TypeConverter
        {
            //获取自定义的TypeConverter 如果获取不到采用默认的
            get { return GetCustomTypeConverter(_propertyDescriptor.PropertyType) ?? _propertyDescriptor.Converter; }
        }

        public override Type TargetType
        {
            get { return _propertyDescriptor.ComponentType; }
        }

        public override Type ReturnType
        {
            get { return _propertyDescriptor.PropertyType; }
        }

        public override string Name
        {
            get { return _propertyDescriptor.Name; }
        }

        public override string FullQualifiedName
        {
            get { return _propertyDescriptor.ComponentType.FullName + "." + _propertyDescriptor.Name; }
        }

        public override bool IsAttached
        {
            get { return false; }
        }

        public override bool IsCollection
        {
            get { return CollectionSupport.IsCollectionType(_propertyDescriptor.PropertyType); }
        }

        public override string Category
        {
            get { return _propertyDescriptor.Category; }
        }

        public override bool IsAdvanced
        {
            get
            {
                var a = _propertyDescriptor.Attributes[typeof(EditorBrowsableAttribute)] as EditorBrowsableAttribute;
                if (a != null)
                {
                    return a.State == EditorBrowsableState.Advanced;
                }
                return false;
            }
        }

        #endregion

        #region CustomTypeConverter

        public static TypeConverter GetCustomTypeConverter(Type propertyType)
        {
            if (propertyType == typeof(object))
            {
                return TypeDescriptor.GetConverter(typeof(string));
            }
            else if (propertyType == typeof(Type))
            {
                return new TypeTypeConverter();
            }
            else if (propertyType == typeof(DependencyProperty))
            {
                return new DependencyPropertyConverter();
            }
            return null;
        }


        sealed class TypeTypeConverter : TypeConverter
        {
            public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
            {
                if (sourceType == typeof(string))
                {
                    return true;
                }
                else
                {
                    return base.CanConvertFrom(context, sourceType);
                }
            }

            public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
            {
                if (value == null)
                {
                    return null;
                }
                if (value is string)
                {
                    IXamlTypeResolver xamlTypeResolver = (IXamlTypeResolver)context.GetService(typeof(IXamlTypeResolver));
                    if (xamlTypeResolver == null)
                    {
                        throw new XamlLoadException("IXamlTypeResolver not found in TypeDescriptor Context");
                    }
                    return xamlTypeResolver.Resolve((string)value);
                }
                else
                {
                    return base.ConvertFrom(context, culture, value);
                }
            }
        }

        sealed class DependencyPropertyConverter : TypeConverter
        {
            public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
            {
                if (sourceType == typeof(string))
                {
                    return true;
                }
                else
                {
                    return base.CanConvertFrom(context, sourceType);
                }
            }

            public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
            {
                if (value == null)
                {
                    return null;
                }
                if (value is string)
                {
                    XamlTypeResolverProvider xamlTypeResolver = (XamlTypeResolverProvider)context.GetService(typeof(XamlTypeResolverProvider));
                    if (xamlTypeResolver == null)
                    {
                        throw new XamlLoadException("XamlTypeResolverProvider not found in TypeDescriptor Context");
                    }
                    XamlPropertyInfo xamlPropertyInfo = xamlTypeResolver.ResolveProperty((string)value);
                    if (xamlPropertyInfo == null)
                    {
                        throw new XamlLoadException("Can't find property " + value + ".");
                    }
                    XamlDependencyPropertyInfo dependencyPropertyInfo = xamlPropertyInfo as XamlDependencyPropertyInfo;
                    if (dependencyPropertyInfo != null)
                    {
                        return dependencyPropertyInfo.DependencyProperty;
                    }

                    FieldInfo fieldInfo = xamlPropertyInfo.TargetType.GetField(xamlPropertyInfo.Name + "Property", BindingFlags.Public | BindingFlags.Static);
                    if (fieldInfo != null && fieldInfo.FieldType == typeof(DependencyProperty))
                    {
                        return (DependencyProperty)fieldInfo.GetValue(null);
                    }
                    throw new XamlLoadException("Property" + value + "is not a dependencyProperty");
                }
                else
                {
                    return base.ConvertFrom(context, culture, value);
                }
            }
        }

        #endregion
        
    }

    #endregion

    #region XamlDependencyPropertyInfo

    public class XamlDependencyPropertyInfo : XamlPropertyInfo
    {
        #region Fields

        private readonly DependencyProperty _dependencyProperty;
        private bool _isAttached;

        #endregion

        #region Constructor

        public XamlDependencyPropertyInfo(DependencyProperty dependencyProperty,bool isAttached)
        {
            if (dependencyProperty == null)
            {
                throw new ArgumentNullException("dependencyProperty");
            }
            _dependencyProperty = dependencyProperty;
            _isAttached = isAttached;
        }

        #endregion

        #region Implements

        public override object GetValue(object instance)
        {
            return ((DependencyObject)instance).GetValue(_dependencyProperty);
        }

        public override void SetValue(object instance, object value)
        {
            ((DependencyObject)instance).SetValue(_dependencyProperty, value);
        }

        public override void ResetValue(object instance)
        {
            ((DependencyObject)instance).ClearValue(_dependencyProperty);
        }

        public override TypeConverter TypeConverter
        {
            get { return TypeDescriptor.GetConverter(ReturnType); }
        }

        public override Type TargetType
        {
            get { return _dependencyProperty.OwnerType; }
        }

        public override Type ReturnType
        {
            get { return _dependencyProperty.PropertyType; }
        }

        public override string Name
        {
            get { return _dependencyProperty.Name; }
        }

        public override string FullQualifiedName
        {
            get { return this.TargetType.FullName + "." + this.Name; }
        }

        public override bool IsAttached
        {
            get { return _isAttached; }
        }

        public override bool IsCollection
        {
            get { return false; }
        }

        public override string Category
        {
            get { return "Misc"; }
        }

        #endregion
        
    }

    #endregion

    #region XamlEventPropertyInfo

    public class XamlEventPropertyInfo : XamlPropertyInfo
    {
        #region Fields

        private EventDescriptor _eventDescriptor;

        #endregion

        #region Constructor

        public XamlEventPropertyInfo(EventDescriptor eventDescriptor)
        {
            _eventDescriptor = eventDescriptor;
        }

        #endregion

        #region Implements

        public override object GetValue(object instance)
        {
            throw new NotSupportedException();
        }

        public override void SetValue(object instance, object value)
        {
            throw new NotSupportedException();
        }

        public override void ResetValue(object instance)
        {
            throw new NotSupportedException();
        }

        public override TypeConverter TypeConverter
        {
            get { return TypeDescriptor.GetConverter(typeof(string)); }
        }

        public override Type TargetType
        {
            get { return _eventDescriptor.ComponentType; }
        }

        public override Type ReturnType
        {
            get { return _eventDescriptor.EventType; }
        }

        public override string Name
        {
            get { return _eventDescriptor.Name; }
        }

        public override string FullQualifiedName
        {
            get { return _eventDescriptor.ComponentType.FullName + "." + _eventDescriptor.Name; }
        }

        public override bool IsAttached
        {
            get { return false; }
        }

        public override bool IsCollection
        {
            get { return false; }
        }

        public override string Category
        {
            get { return _eventDescriptor.Category; }
        }

        #endregion
       
    }

    #endregion
  
}
